// #![allow(unused)]
use std::{collections::HashMap, sync::OnceLock};

use cgmath::Vector2;
use image::Rgba;

use crate::renderer::{Texture, Renderer};

use crate::TEXTURES;
use crate::camera::*;
use crate::util::*;
use crate::{WIDTH, HEIGHT};

#[allow(non_snake_case)]
pub fn CACTUS_INFOS() -> &'static Vec<(String, Vector2<f64>)> {
    static CACTUS_TEXTURE: OnceLock<Vec<(String, Vector2<f64>)>> = OnceLock::new();
    CACTUS_TEXTURE.get_or_init(|| {
        vec![
            ("Cactus_BIG1"  .into(), Vector2::new( 70.0,  140.0)),
            ("Cactus_BIG2"  .into(), Vector2::new(120.0,  120.0)),
            ("Cactus_MIX"   .into(), Vector2::new(120.0,  80.0)),
            ("Cactus_SMALL1".into(), Vector2::new( 25.0,  50.0)),
            ("Cactus_SMALL2".into(), Vector2::new( 50.0,  50.0)),
            ("Cactus_SMALL3".into(), Vector2::new( 75.0,  50.0)),
        ]
    })
}


pub struct Cactus {
    #[allow(unused)]
    cactus_type: i64,

    pic_size: Vector2<f64>,
    position: Vector2<f64>,
    velocity: Vector2<f64>,

    texture: Texture,

    collision_box: (Vector2<f64>, Vector2<f64>),
}

pub struct Pterosaur {
    pic_size: Vector2<f64>,
    position: Vector2<f64>,
    velocity: Vector2<f64>,

    textures: HashMap<String, (Texture, f64, String)>,
    animation_timer: f64,
    current_texture: String,

    collision_box: (Vector2<f64>, Vector2<f64>),
}

pub struct Meteorite {
    pic_size: Vector2<f64>,
    position: Vector2<f64>,
    velocity: Vector2<f64>,

    texture: Texture,

    collision_box: (Vector2<f64>, Vector2<f64>),
}

pub trait Obstruction {
    fn update(
        &mut self,
        delta_time: f64
    );

    fn draw(
        &self,
        renderer: &mut Renderer,
        camera: &Camera,
    );

    fn get_position(&self) -> Vector2<f64>;

    fn get_velocity(&self) -> Vector2<f64>;

    fn get_size(&self) -> Vector2<f64>;

    fn get_current_collision_box(&self) -> (Vector2<f64>, Vector2<f64>);
}

impl Cactus {
    pub fn gen(
        cactus_type: i64,
        position: Vector2<f64>,
        velocity: Vector2<f64>,
    ) -> Self {
        let pic_size = CACTUS_INFOS()[cactus_type as usize].1;
        Self {
            cactus_type,

            pic_size,
            position,
            velocity,
            
            texture: TEXTURES()[&CACTUS_INFOS()[cactus_type as usize].0].clone(),

            collision_box: (
                Vector2::new(-0.4 * pic_size.x, 0.9 * pic_size.y),
                Vector2::new( 0.4 * pic_size.x, 0.1 * pic_size.y)
            )
        }
    }

    pub fn get_position(&self) -> Vector2<f64> {
        self.position
    }

    pub fn get_velocity(&self) -> Vector2<f64> {
        self.velocity
    }

    pub fn get_size(&self) -> Vector2<f64> {
        self.pic_size
    }

    pub fn get_current_collision_box(
        &self
    ) -> (Vector2<f64>, Vector2<f64>) {
        self.collision_box
    }

    pub fn draw_collision_box(
        &self,
        renderer: &mut Renderer,
        camera: &Camera,
    ) {
        let screen_size = Vector2::new(WIDTH as f64, HEIGHT as f64);
        let (lu_w, rd_w) = (
            self.get_current_collision_box().0 + self.position,
            self.get_current_collision_box().1 + self.position,
        );
        let lu_s = world_to_screen(&lu_w, camera, &screen_size);
        let rd_s = world_to_screen(&rd_w, camera, &screen_size);

        renderer.draw_rect(
            &lu_s,
            &rd_s,
            Rgba([255, 0, 0, 255]),
        );
    }
}

impl Obstruction for Cactus {
    fn update(
        &mut self,
        delta_time: f64
    ) {
        self.position = self.position + self.velocity * delta_time;
    }

    fn draw(
        &self,
        renderer: &mut Renderer,
        camera: &Camera,
    ) {
        let leftup_world =
            Vector2::new(
                self.position.x - self.pic_size.x * 0.5,
                self.position.y + self.pic_size.y,
            );
        let rightdown_world =
            Vector2::new(
                self.position.x + self.pic_size.x * 0.5,
                self.position.y,
            );

        let leftup_screen = world_to_screen(
            &leftup_world,
            camera,
            &Vector2::new(WIDTH as _, HEIGHT as _)
        );
        let rightdown_screen = world_to_screen(
            &rightdown_world,
            camera,
            &Vector2::new(WIDTH as _, HEIGHT as _)
        );

        renderer.draw_texture(
            &leftup_screen,
            &rightdown_screen,
            &self.texture
        );
    }

    fn get_position(&self) -> Vector2<f64> {
        self.position
    }

    fn get_velocity(&self) -> Vector2<f64> {
        self.velocity
    }

    fn get_size(&self) -> Vector2<f64> {
        self.pic_size
    }

    fn get_current_collision_box(
        &self
    ) -> (Vector2<f64>, Vector2<f64>) {
        self.collision_box
    }
}

impl Pterosaur {
    pub fn gen(
        position: Vector2<f64>,
        velocity: Vector2<f64>,
    ) -> Self {
        let texture_names: Vec<String> = vec![
            "Bird1".into(),
            "Bird2".into(),
        ];
        let texture_infos = vec![
            (&texture_names[0], 0.1, &texture_names[1]),
            (&texture_names[1], 0.2, &texture_names[0]),
        ];
        let mut textures = HashMap::new();
        
        for i in 0..texture_names.len() {
            let texture = TEXTURES()[&texture_names[i]].clone();
            textures.insert(
                texture_infos[i].0.clone(),
                (texture, texture_infos[i].1, texture_infos[i].2.clone()), 
            );
        }

        let pic_size = Vector2::new(120.0, 90.0);

        Self {
            pic_size,
            position,
            velocity,
            
            textures,
            current_texture: "Bird1".into(),
            animation_timer: 0.1,

            collision_box: (
                Vector2::new(-0.4 * pic_size.x, 0.9 * pic_size.y),
                Vector2::new( 0.4 * pic_size.x, 0.1 * pic_size.y)
            )
        }
    }

    pub fn draw_collision_box(
        &self,
        renderer: &mut Renderer,
        camera: &Camera,
    ) {
        let screen_size = Vector2::new(WIDTH as f64, HEIGHT as f64);
        let (lu_w, rd_w) = (
            self.get_current_collision_box().0 + self.position,
            self.get_current_collision_box().1 + self.position,
        );
        let lu_s = world_to_screen(&lu_w, camera, &screen_size);
        let rd_s = world_to_screen(&rd_w, camera, &screen_size);

        renderer.draw_rect(
            &lu_s,
            &rd_s,
            Rgba([255, 0, 0, 255]),
        );
    }
}

impl Obstruction for Pterosaur {
    fn update(
        &mut self,
        delta_time: f64
    ) {
        self.position = self.position + self.velocity * delta_time;

        self.animation_timer -= delta_time;
        if self.animation_timer <= 0.0 {
            self.current_texture =
                self.textures[&self.current_texture].2.clone();
            self.animation_timer =
                self.textures[&self.current_texture].1;

        }
    }

    fn draw(
        &self,
        renderer: &mut Renderer,
        camera: &Camera,
    ) {
        let leftup_world =
            Vector2::new(
                self.position.x - self.pic_size.x * 0.5,
                self.position.y + self.pic_size.y,
            );
        let rightdown_world =
            Vector2::new(
                self.position.x + self.pic_size.x * 0.5,
                self.position.y,
            );

        let leftup_screen = world_to_screen(
            &leftup_world,
            camera,
            &Vector2::new(WIDTH as _, HEIGHT as _)
        );
        let rightdown_screen = world_to_screen(
            &rightdown_world,
            camera,
            &Vector2::new(WIDTH as _, HEIGHT as _)
        );

        renderer.draw_texture(
            &leftup_screen,
            &rightdown_screen,
            &self.textures[&self.current_texture].0,
        );
    }

    fn get_position(&self) -> Vector2<f64> {
        self.position
    }

    fn get_velocity(&self) -> Vector2<f64> {
        self.velocity
    }

    fn get_size(&self) -> Vector2<f64> {
        self.pic_size
    }

    fn get_current_collision_box(
        &self
    ) -> (Vector2<f64>, Vector2<f64>) {
        self.collision_box
    }
}

impl Meteorite {
    pub fn gen(
        position: Vector2<f64>,
        velocity: Vector2<f64>,
    ) -> Self {
        let pic_size = Vector2::new(50.0, 50.0);
        Self {
            pic_size,
            position,
            velocity,
            
            texture: TEXTURES()["Meteorite"].clone(),

            collision_box: (
                Vector2::new(-0.4 * pic_size.x, 0.6 * pic_size.y),
                Vector2::new( 0.4 * pic_size.x, 0.2 * pic_size.y)
            )
        }
    }

    pub fn get_position(&self) -> Vector2<f64> {
        self.position
    }

    pub fn get_velocity(&self) -> Vector2<f64> {
        self.velocity
    }

    pub fn get_size(&self) -> Vector2<f64> {
        self.pic_size
    }

    pub fn get_current_collision_box(
        &self
    ) -> (Vector2<f64>, Vector2<f64>) {
        self.collision_box
    }

    pub fn draw_collision_box(
        &self,
        renderer: &mut Renderer,
        camera: &Camera,
    ) {
        let screen_size = Vector2::new(WIDTH as f64, HEIGHT as f64);
        let (lu_w, rd_w) = (
            self.get_current_collision_box().0 + self.position,
            self.get_current_collision_box().1 + self.position,
        );
        let lu_s = world_to_screen(&lu_w, camera, &screen_size);
        let rd_s = world_to_screen(&rd_w, camera, &screen_size);

        renderer.draw_rect(
            &lu_s,
            &rd_s,
            Rgba([255, 0, 0, 255]),
        );
    }
}

impl Obstruction for Meteorite {
    fn update(
        &mut self,
        delta_time: f64
    ) {
        self.position = self.position + self.velocity * delta_time;
    }

    fn draw(
        &self,
        renderer: &mut Renderer,
        camera: &Camera,
    ) {
        let leftup_world =
            Vector2::new(
                self.position.x - self.pic_size.x * 0.5,
                self.position.y + self.pic_size.y,
            );
        let rightdown_world =
            Vector2::new(
                self.position.x + self.pic_size.x * 0.5,
                self.position.y,
            );

        let leftup_screen = world_to_screen(
            &leftup_world,
            camera,
            &Vector2::new(WIDTH as _, HEIGHT as _)
        );
        let rightdown_screen = world_to_screen(
            &rightdown_world,
            camera,
            &Vector2::new(WIDTH as _, HEIGHT as _)
        );

        renderer.draw_texture(
            &leftup_screen,
            &rightdown_screen,
            &self.texture
        );
    }

    fn get_position(&self) -> Vector2<f64> {
        self.position
    }

    fn get_velocity(&self) -> Vector2<f64> {
        self.velocity
    }

    fn get_size(&self) -> Vector2<f64> {
        self.pic_size
    }

    fn get_current_collision_box(
        &self
    ) -> (Vector2<f64>, Vector2<f64>) {
        self.collision_box
    }
}